<!-- 
  首先要明确，合并是依赖span-method方法return[rowIndex, colIndex]，
  rowIndex是几，就表示从当前单元格坐标的开始到第rowIndex行合并，
  colIndex是几，就表示从当前单元格坐标的开始到第colIndex列合并。

  el-table合并列的思路：
    
 -->
 <template>
  <div class="table">
    <el-table :data="tableData" :span-method="objectSpanMethods" border style="width: 100%">
      <template v-for="cols in colConfigs">
        <!-- 无需合并的列 -->
        <el-table-column
          v-if="cols.type === 'label' && !cols.children"
          :key="cols.prop"
          :prop="cols.prop"
          :label="cols.label"
        >
        </el-table-column>
        <!-- 需要合并的列 -->
        <template v-else-if="cols.type === 'label' && cols.children">
          <el-table-column
            v-for="children in cols.children"
            :key="children.prop"
            :prop="children.prop"
            :label="children.label"
          />
        </template>
      </template>
    </el-table>
  </div>

</template>

<script lang="ts" setup>
import GeneralTable from '@/components/GeneralTable.vue'
import { ref, onMounted } from 'vue'

const tableData: any = ref([
  { age: '1', time: '2020-08-10', grade: '三年二班', name: '小明', subjects: '语文', score: 80 },
  { age: '1', time: '2020-08-10', grade: '三年二班', name: '小明', subjects: '数学', score: 80 }, 
  { age: '1', time: '2020-08-10', grade: '总成绩', name: '总成绩', subjects: '总成绩', score: 160 },
  { age: '1', time: '2020-08-10', grade: '三年一班', name: '小雷', subjects: '语文', score: 70 },
  { age: '1', time: '2020-08-10', grade: '三年一班', name: '小雷', subjects: '数学', score: 80 },
  { age: '1', time: '2020-08-10', grade: '总成绩', name: '总成绩', subjects: '总成绩', score: 150 }, 
  { age: '1', time: '2020-08-11', grade: '三年三班', name: '小花', subjects: '语文', score: 60 }, 
  { age: '1', time: '2020-08-11', grade: '三年三班', name: '小花', subjects: '数学', score: 60 }, 
  { age: '1', time: '2020-08-11', grade: '总成绩', name: '总成绩', subjects: '总成绩', score: 120 }
])

// 需要合并 
const colConfigs = [
  {
    type: 'label',
    children: [
      { prop: 'time', label: '时间' },
      { prop: 'grade', label: '年级' },
      { prop: 'name', label: '姓名' },
      { prop: 'subjects', label: '科目' },
      { prop: 'score', label: '成绩' },
      // { prop: 'age', label: '年龄'},
    ]
  },
  { type: 'label', prop: 'age', label: '年龄'},
]

/**
 * 需要合并的行列信息
 * index  所在的列下标
 * name   要合并的字段名称
 */
const mergeCols = ref([
  { index: 0, name: 'time' },
  { index: 1, name: 'grade' },
  { index: 2, name: 'name' },
  { index: 3, name: 'subjects' },
  { index: 4, name: 'score' },
])

// 用来记录每一个单元格的下标
const tableMergeIndex: any = ref([])

onMounted(() => {
  if(mergeCols.value.length > 0) {
    newTableMergeData();
  }
})

const objectSpanMethods = ({ row, column, rowIndex, columnIndex }: any) => {
  let key = columnIndex + '_' + rowIndex;
  if (tableMergeIndex.value[key]) {
    return tableMergeIndex.value[key];
  }
}

const newTableMergeData = () => {
  // 遍历表格中需要合并的所有单元格
  for (let i = 0; i < tableData.value.length; i++) {
    for (let j = 0; j < mergeCols.value.length; j++) {
      // 初始化行、列坐标信息
      let rowIndex = 1;
      let columnIndex = 1;
      // 将当前横坐标中的元素与左方第一个元素相比较, 若相等：columnIndex = 0,表示列不用合并
      if (j > 0 && tableData.value[i][mergeCols.value[j]['name']] === tableData.value[i][mergeCols.value[j - 1]['name']]) {
        columnIndex = 0;
      }
      // 比较纵坐标上方的第一个元素
      if (i > 0 && tableData.value[i][mergeCols.value[j]['name']] === tableData.value[i - 1][mergeCols.value[j]['name']]) {
        rowIndex = 0;
      }
      // 将当前横坐标中的元素与右方元素相比较
      if (columnIndex > 0) {
        columnIndex = onColIndex(tableData.value[i], j, j + 1, 1, mergeCols.value.length);
      }
      // 比较纵坐标下方元素
      if (rowIndex > 0) {
        rowIndex = onRowIndex(tableData.value, i, i + 1, 1, mergeCols.value[j]['name']);
      }
      let key = mergeCols.value[j]['index'] + '_' + i;
      tableMergeIndex.value[key] = [rowIndex, columnIndex];
    }
  }
}
/**
  * 计算列坐标信息
  * data 单元格所在行数据
  * index 当前下标
  * nextIndex 下一个元素下标
  * count 相同内容的数量
  * maxLength 当前行的列总数
  */
const onColIndex: any = (data: any, index: number, nextIndex: number, count: number, maxLength: number) => {
  // 比较当前单元格中的数据与同一行之后的单元格是否相同，若相同，便接着比较当前单元格与再下一个是否相同...以此类推，直到出现不同
  if (nextIndex < maxLength && data[mergeCols.value[index]['name']] === data[mergeCols.value[nextIndex]['name']]) {
    return onColIndex(data, index, ++nextIndex, ++count, maxLength);
  }
  return count;
}
/**
  * 计算行坐标信息
  * data 表格总数据
  * index 当前下标
  * nextIndex 下一个元素坐标
  * count 相同内容的数量
  * name 数据的key
  */
const onRowIndex: any = (data: any, index: number, nextIndex: number, count: number, name: any) => {
  // 比较当前单元格中的数据与同一列之后的单元格是否相同
  if (nextIndex < data.length && data[index][name] === data[nextIndex][name]) {
    return onRowIndex(data, index, ++nextIndex, ++count, name);
  }
  return count;
}
</script>
